package com.znyj.cloud.chain.tcphandler;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.znyj.cloud.chain.util.CheckUtil;
import com.znyj.cloud.device.constant.SysConstant;
import com.znyj.cloud.device.executor.ReportExecutorService;
import com.znyj.cloud.device.executor.TaskExecutorService;
import com.znyj.cloud.device.pack.*;
import com.znyj.cloud.device.task.OfflineDeviceMapping;
import com.znyj.cloud.device.task.OnlineDeviceInfoMapping;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.sql.Timestamp;

/**
 * Created by gaoliwei on 15-1-28.
 */
@ChannelHandler.Sharable
@Component
public class PackageDispatcherHandler extends SimpleChannelInboundHandler<BasePackage> {

    private static final Logger logger = Logger.getLogger(PackageDispatcherHandler.class);

    @Resource
    private OfflineDeviceMapping offlineDeviceMapping;
    @Resource
    private ReportExecutorService reportExecutorService;
    @Resource
    private TaskExecutorService taskExecutorService;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, BasePackage receiveBasePackage) throws Exception {
        try {
            if (SysConstant.DEBUG_DISPLAY)
                logger.info(receiveBasePackage.getJsonObject().toString());

            //如果是二进制数据处理
            if (receiveBasePackage.getFormat() == SysConstant.HEAD.FORMAT_BYTE) {
                logger.info("pay attention this package is format byte!");
            }
            //如果是JSON数据处理
            else if (receiveBasePackage.getFormat() == SysConstant.HEAD.FORMAT_JSON) {
                String msgType = receiveBasePackage.getJsonObject().getString(SysConstant.REPORT_KEY.MSG_TYPE);
                JSONObject jsonObject = receiveBasePackage.getJsonObject();
                //选择数据类型
                switch (msgType) {
                    //接收的类型为设备心跳包
                    case SysConstant.PACKAGE_VALUE.HAERTBEAT_RECEIVE: {
                        ReportReceivePackage reportReceivePackage = new ReportReceivePackage();
                        BeanUtils.copyProperties(receiveBasePackage, reportReceivePackage);
                        reportReceivePackage.parseJsonData(jsonObject);

                        //输出到elasticsearch
                        logger.info(CheckUtil.dataReport("@hb_pac@", new Gson().toJson(reportReceivePackage.getReportEntity())));

                        //从在线设备里获取在线设备信息
                        String deviceId = reportReceivePackage.getReportEntity().getDeviceId();
                        OnlineDeviceInfo onlineDeviceInfo = OnlineDeviceInfoMapping.getODIByDeviceId(deviceId);

                        //在Map中存在
                        if (onlineDeviceInfo != null) {
                            String lastIp = onlineDeviceInfo.getCtx().channel().remoteAddress().toString();
                            String nowIp = ctx.channel().remoteAddress().toString();

                            //一般情况，更新一下MAP和数据库
                            if (lastIp.equals(nowIp)) {
                                OnlineDeviceInfoMapping.update(deviceId);
                                reportExecutorService.executeUpdateReport(reportReceivePackage, nowIp);
                            }
                            //IP改变了，删除原ctx、更改onlineMap的ctx
                            else {
                                OnlineDeviceInfoMapping.updateNewChannel(deviceId, ctx);
                                reportExecutorService.executeUpdateReport(reportReceivePackage, nowIp);
                                logger.error(CheckUtil.monitorReport(SysConstant.ERROR_CODE.RECONNECT, "LAST IP:" + lastIp + " NOW IP:" + nowIp, Long.parseLong(deviceId)));
                            }
                        }
                        //刚上线或第一次上线、插入到onlineMap 插入数据库
                        else {
                            OnlineDeviceInfoMapping.add(deviceId, ctx, SysConstant.TIMEOUT_HEARTBEAT);
                            reportExecutorService.executeFirstInsert(reportReceivePackage);
                            logger.info("device register :{}" + reportReceivePackage.getReportEntity().getDeviceId());
                            //每次刚上线会更新设备绑定的poiid
                            reportExecutorService.executeUpdatePoiid(reportReceivePackage.getReportEntity().getDeviceId());
                        }
//                        //插入report数据库-------此通道第一次在内存设备列表中出现：1.新上设备 2.设备换了一个通道 3.刚重启服务器内存丢失
//                        if (OnlineDeviceInfoMapping.getByChannel(ctx) == null) {
//                            //如果deviceId换了一个TCP通道
//                            if (OnlineDeviceInfoMapping.contain(cdbReportReceivePackage.getCdbReportEntity().getDeviceId())) {
//
//                                logger.error(CheckUtil.monitorReport(SysConstant.ERROR_CODE.RECONNECT, ">>>*IP:" + ctx.channel().remoteAddress().toString(), deviceId);
//
//                                //从deviceId 找到旧的OnlineDeviceInfo，然后删除
//                                OnlineDeviceInfoMapping.remove(OnlineDeviceInfoMapping.getByDeviceId(cdbReportReceivePackage.getCdbReportEntity().getDeviceId()));
//                            }
//                            //添加到内存中
//                            OnlineDeviceInfoMapping.add(cdbReportReceivePackage.getCdbReportEntity().getDeviceId(), cdbReportReceivePackage.getCdbReportEntity().getBatteryStatus(), ctx);
//                            //插入新的deviceId心跳，如果存在不插入
//                            cdbReportExecutorService.executeFirstInsert(cdbReportReceivePackage);
//                        }

                        //放进SQL FIFO池子 !!!!不再使用这个，不合适
                        //SqlTempPool.sqlReportQueue.offer(cdbReportEntity);

                        //回复心跳包发送
                        ReportSendPackage reportSendPackage = new ReportSendPackage(reportReceivePackage.getReportEntity().getDeviceId());
                        reportSendPackage.generateAndSendPackage(ctx);

                        //输出到elasticsearch
                        logger.info(CheckUtil.dataReport("@hb_res_pac@", reportSendPackage.reportJson()));

                        break;
                    }

                    //接收为控制回包
                    case SysConstant.PACKAGE_VALUE.CTRL_RES_RECEIVE: {
                        TaskReceivePackage taskReceivePackage = new TaskReceivePackage();
                        BeanUtils.copyProperties(receiveBasePackage, taskReceivePackage);
                        taskReceivePackage.parseJsonData(jsonObject);

                        //输出到elasticsearch
                        logger.info(CheckUtil.dataReport("@ctrl_res_pac@", taskReceivePackage.reportJson()));

                        //更新收到的数据到数据库
                        taskExecutorService.executeUpdateTask(taskReceivePackage);
                        break;
                    }

                    //接收为升级回包
                    case SysConstant.PACKAGE_VALUE.UP_RES_RECEIVE: {
                        TaskReceivePackage taskReceivePackage = new TaskReceivePackage();
                        BeanUtils.copyProperties(receiveBasePackage, taskReceivePackage);

                        //回包的BUG
                        taskReceivePackage.setDeviceId(jsonObject.getString(SysConstant.UP_KEY.IMEI));
                        try {
                            taskReceivePackage.setOrderId(jsonObject.getLong(SysConstant.CTRL_KEY.ORDER_ID));
                        } catch (Exception e) {
                            taskReceivePackage.setOrderId(0);
                        }
                        taskReceivePackage.setReportTime(new Timestamp(System.currentTimeMillis()));
                        taskReceivePackage.setReturnMsg(jsonObject.getString(SysConstant.UP_KEY.UP_STATUS));

                        //输出到elasticsearch
                        logger.info(CheckUtil.dataReport("@up_res_pac@", taskReceivePackage.reportJson()));

                        //更新收到的数据到数据库
                        taskExecutorService.executeUpdateTask(taskReceivePackage);
                        break;
                    }

                    default: {
                        logger.error("fire get invalid msgType!");
                        ctx.fireChannelInactive();
                        ctx.close();
                        break;
                    }

                }
            }
        } catch (Exception e) {
            logger.error("channelRead0 exception.", e);
        }
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (cause instanceof IOException) {
            return;
        }
        super.exceptionCaught(ctx, cause);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        offlineDeviceMapping.deviceOffLine(ctx);
        super.channelInactive(ctx);
    }
}
